package cn.xinfei.xdecision.common.service.component.guiderule.impl;

import cn.hutool.core.date.DateUtil;
import cn.xinfei.xdecision.common.dao.component.guiderule.RuleVersionMapper;
import cn.xinfei.xdecision.common.model.common.PageResult;
import cn.xinfei.xdecision.common.model.common.ResponseEntityDto;
import cn.xinfei.xdecision.common.model.common.requestParam.AssemblyRuleCodeReq;
import cn.xinfei.xdecision.common.model.common.requestParam.StatusParam;
import cn.xinfei.xdecision.common.model.component.ComponentAction;
import cn.xinfei.xdecision.common.model.component.guiderule.RuleBlock;
import cn.xinfei.xdecision.common.model.component.guiderule.RuleConditionInfo;
import cn.xinfei.xdecision.common.model.component.guiderule.RuleInfo;
import cn.xinfei.xdecision.common.model.component.guiderule.RuleVersion;
import cn.xinfei.xdecision.common.model.component.guiderule.vo.RuleBlockVo;
import cn.xinfei.xdecision.common.model.component.guiderule.vo.RuleConditionVo;
import cn.xinfei.xdecision.common.model.component.guiderule.vo.RuleVersionRespVo;
import cn.xinfei.xdecision.common.model.component.guiderule.vo.RuleVersionVo;
import cn.xinfei.xdecision.common.model.component.strategyout.StrategyOutput;
import cn.xinfei.xdecision.common.model.login.SessionManager;
import cn.xinfei.xdecision.common.service.common.sso.SSOService;
import cn.xinfei.xdecision.common.service.component.ComponentCommonContext;
import cn.xinfei.xdecision.common.service.component.componentaction.ComponentActionService;
import cn.xinfei.xdecision.common.service.component.guiderule.RuleBlockService;
import cn.xinfei.xdecision.common.service.component.guiderule.RuleConditionService;
import cn.xinfei.xdecision.common.service.component.guiderule.RuleService;
import cn.xinfei.xdecision.common.service.component.guiderule.RuleVersionService;
import cn.xinfei.xdecision.common.service.component.strategyout.StrategyOutputService;
import cn.xinfei.xdecision.common.utils.constant.SysConstant;
import cn.xinfei.xdecision.common.utils.constant.strategyx.StrategyType;
import cn.xinfei.xdecision.common.utils.enums.enginex.ComponentStatusEnum;
import cn.xinfei.xdecision.common.utils.enums.enginex.ComponentTypeEnum;
import cn.xinfei.xdecision.common.utils.enums.enginex.ComponentVersionStatusEnum;
import cn.xinfei.xdecision.common.utils.exception.ApiException;
import cn.xinfei.xdecision.common.utils.exception.enums.ErrorCodeEnum;
import cn.xinfei.xdecision.common.utils.util.BeanUtilsPlus;
import cn.xinfei.xdecision.common.utils.util.StringUtil;
import cn.xinfei.xdecision.common.utils.util.strategyx.CustomValueUtils;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageInfo;
import com.github.pagehelper.page.PageMethod;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;

import static cn.xinfei.xdecision.common.utils.exception.enums.ErrorCodeEnum.*;
import static cn.xinfei.xdecision.common.utils.exception.util.ApiExceptionUtil.exception;

@Service
@Slf4j
public class RuleVersionServiceImpl extends ServiceImpl<RuleVersionMapper, RuleVersion> implements RuleVersionService {
    @Autowired
    private RuleVersionMapper versionMapper;
    @Autowired
    private RuleBlockService ruleBlockService;
    @Autowired
    private RuleConditionService conditionService;
    @Autowired
    private StrategyOutputService strategyOutputService;
    @Resource
    private RuleService ruleService;
    @Resource
    private TransactionTemplate transactionTemplate;
    @Resource
    private SSOService ssoService;

    @Resource
    private ComponentActionService componentActionService;

    @Override
    public RuleVersionVo queryById(Long versionId) {
        RuleVersion ruleVersion = versionMapper.selectById(versionId);

        RuleVersionVo result = new RuleVersionVo();
        if (ruleVersion == null) {
            return result;
        }
        BeanUtils.copyProperties(ruleVersion, result);
        result.setStatusDesc(Objects.requireNonNull(ComponentVersionStatusEnum.get(ruleVersion.getStatus())).getName());

        List<RuleBlock> ruleBlockList = ruleBlockService.listObjectsByVersionId(versionId);
        List<RuleBlockVo> ruleBlockVoList = new ArrayList<>();
        for (RuleBlock ruleBlock : ruleBlockList) {
            RuleBlockVo ruleBlockVo = new RuleBlockVo();
            BeanUtils.copyProperties(ruleBlock, ruleBlockVo);
            //查询ruleCondition组装成树形结构
            RuleConditionVo ruleConditionVo = conditionService.queryByBlockId(ruleBlock.getId());
            List<StrategyOutput> strategyOutputList = strategyOutputService.queryByTactics(new StrategyOutput(ruleBlock.getId(), ComponentTypeEnum.COMPLEX_RULE_BASE.getType(), StrategyType.OutType.SUCCESS_OUT));
            List<StrategyOutput> failOutputList = strategyOutputService.queryByTactics(new StrategyOutput(ruleBlock.getId(), ComponentTypeEnum.COMPLEX_RULE_BASE.getType(), StrategyType.OutType.FAIL_OUT));
            ruleBlockVo.setRuleConditionVo(ruleConditionVo);
            ruleBlockVo.setStrategyOutputList(strategyOutputList);
            ruleBlockVo.setFailOutputList(failOutputList);
            ruleBlockVoList.add(ruleBlockVo);
        }
        result.setRuleBlockVoList(ruleBlockVoList);
        result.setVersionNoDesc("V" + result.getVersionNo());
        result.setComponentActionList(componentActionService.getComponentActionByComponentCode(
                ruleVersion.getRuleCode(),
                ruleVersion.getVersionNo(),
                Collections.singletonList(ComponentTypeEnum.COMPLEX_RULE_BASE.getType())));
        return result;
    }

    @Override
    public RuleVersion getVersionByVersionNo(String ruleCode, Long versionNo) {
        LambdaQueryWrapper<RuleVersion> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(RuleVersion::getRuleCode, ruleCode);
        queryWrapper.eq(RuleVersion::getVersionNo, versionNo);
        return this.getOne(queryWrapper);
    }

    @Override
    public List<RuleVersion> getVersionByStatus(String ruleCode, int status) {
        QueryWrapper<RuleVersion> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("rule_code", ruleCode);
        queryWrapper.eq("status", status);

        return this.list(queryWrapper);
    }

    @Override
    public List<RuleVersionVo> queryVersionListByRuleId(Long ruleId) {
        LambdaQueryWrapper<RuleVersion> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(RuleVersion::getRuleId, ruleId);
        queryWrapper.orderByDesc(RuleVersion::getUpdatedTime);
        List<RuleVersion> ruleVersionList = versionMapper.selectList(queryWrapper);
        List<RuleVersionVo> ruleVersionVoList = new ArrayList<>();
        for (RuleVersion ruleVersion : ruleVersionList) {
            RuleVersionVo versionVo = new RuleVersionVo();
            BeanUtils.copyProperties(ruleVersion, versionVo);
            ruleVersionVoList.add(versionVo);
        }
        return ruleVersionVoList;
    }

    @Override
    public List<RuleVersionVo> queryVersionInfoByRuleId(Long ruleId) {
        List<RuleVersionVo> ruleVersionVos = queryVersionListByRuleId(ruleId);
        if (ruleVersionVos.isEmpty()) {
            return ruleVersionVos;
        }
        for (RuleVersionVo ruleVersionVo : ruleVersionVos) {
            RuleVersionVo ruleVersionVo1 = queryById(ruleVersionVo.getId());
            ruleVersionVo.setRuleBlockVoList(ruleVersionVo1.getRuleBlockVoList());
            ruleVersionVo.setComponentActionList(
                    componentActionService.getComponentActionByComponentCode(
                            ruleVersionVo1.getRuleCode(),
                            ruleVersionVo1.getVersionNo(),
                            Collections.singletonList(ComponentTypeEnum.COMPLEX_RULE_BASE.getType())));
        }
        return ruleVersionVos;
    }


    @Override
    public List<RuleVersion> queryVersionByRuleCodes(List<String> ruleCodes, Integer status) {
        if (CollectionUtils.isEmpty(ruleCodes)) {
            return new ArrayList<>();
        }
        LambdaQueryWrapper<RuleVersion> lbd = new LambdaQueryWrapper<RuleVersion>()
                .in(RuleVersion::getRuleCode, ruleCodes);
        if (Objects.nonNull(status)) {
            lbd.eq(RuleVersion::getStatus, status);
        }
        return this.list(lbd);
    }

    @Override
    public List<RuleVersionVo> queryVersionInfoByRuleCode(String ruleCode) {
        if (StringUtil.isBlank(ruleCode)) {
            throw new ApiException(PARAMS_EXCEPTION.getCode(), PARAMS_EXCEPTION.getMessage());
        }

        RuleInfo ruleInfo = ruleService.queryByCode(ruleCode);
        if (ruleInfo == null) {
            return null;

        }
        return queryVersionInfoByRuleId(ruleInfo.getId());
    }

    @Override
    public ResponseEntityDto<PageResult<List<RuleVersionRespVo>, RuleVersion>> listByRuleCode(AssemblyRuleCodeReq req) {
        PageMethod.startPage(req.getPageNo(), req.getPageSize());

        List<RuleVersion> list = this.list(new LambdaQueryWrapper<RuleVersion>()
                .eq(RuleVersion::getRuleCode, req.getRuleCode())
                .orderByDesc(RuleVersion::getUpdatedTime));

        List<RuleVersionRespVo> ruleVersionRespVos =
                BeanUtilsPlus.copyElements(list, RuleVersionRespVo::new);
        if (CollectionUtils.isEmpty(list)) {
            return PageResult.success(new ArrayList<>(), new PageInfo<>());
        }
        ssoService.fillUserInfoToVO(ruleVersionRespVos);

        ruleVersionRespVos.forEach(x -> {
            x.setStatusDesc(Objects.requireNonNull(ComponentVersionStatusEnum.get(x.getStatus())).getName());
            x.setVersionNoDesc("V" + x.getVersionNo());
        });
        return PageResult.success(ruleVersionRespVos, new PageInfo<>(list));
    }

    @Override
    public List<String> queryFieldEnByVersionId(Long versionId) {
        Set<String> fieldEns = new HashSet<>();
        List<RuleBlock> ruleBlockList = ruleBlockService.listObjectsByVersionId(versionId);
        for (RuleBlock ruleBlock : ruleBlockList) {
            RuleConditionVo ruleConditionVo = conditionService.queryByBlockId(ruleBlock.getId());
            List<RuleConditionInfo> ruleConditionInfoList = conditionService.disassemble(ruleConditionVo, ruleBlock.getRuleId(), false);
            for (RuleConditionInfo info : ruleConditionInfoList) {
                if (info.getFieldCode() != null && info.getValueType() != 1 && !info.getFieldCode().startsWith("%")) {
                    if (info.getFieldCode().contains(".")) {
                        fieldEns.add(info.getFieldCode().split("\\.")[0]);
                    } else {
                        fieldEns.add(info.getFieldCode());
                    }
                } else if (StringUtils.isNotEmpty(info.getFieldCode())) {
                    fieldEns.add(info.getFieldCode());
                }
                if (info.getValueType() != null) {
                    if (info.getValueType() == 2 && info.getValue() != null && !info.getValue().contains("%")) {
                        fieldEns.add(info.getValue());
                    } else if (info.getValueType() == 3) {
                        fieldEns.addAll(CustomValueUtils.getFieldEnSet(info.getValue()));
                    }
                }
            }
        }
        return new ArrayList<>(fieldEns);
    }

    @Override
    @Transactional
    public List<Long> addVersionList(List<RuleVersionVo> versionList) {
        List<Long> newVersionIds = new ArrayList<>();
        for (RuleVersionVo versionVo : versionList) {
            newVersionIds.add(addVersion(versionVo));
        }
        return newVersionIds;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long checkAndAddVersion(RuleVersionVo version) {
        if (StringUtils.isEmpty(version.getRuleCode())) {
            throw new ApiException(PARAMS_EXCEPTION.getCode(), PARAMS_EXCEPTION.getMessage());
        }
        RuleInfo ruleInfo = ruleService.queryByCode(version.getRuleCode());
        if (ruleInfo == null) {
            throw exception(COMPONENT_NOT_EXISTS, version.getRuleCode());
        }
        //更新规则的更新人，更新时间
        ruleInfo.setUpdatedTime(DateUtil.now());
        ruleInfo.setUpdateUserId(SessionManager.getLoginUserId());
        if (!ruleService.updateById(ruleInfo)) {
            throw new ApiException(ErrorCodeEnum.DECISION_VERSION_NEW_ERROR);
        }

        //填充规则属性值
        version.setRuleId(ruleInfo.getId());
        //查询规则的版本信息
        List<RuleVersionVo> ruleVersionVos = queryVersionInfoByRuleCode(version.getRuleCode());
        RuleVersionVo newVersion = ruleVersionVos.stream()
                .filter(x -> Objects.equals(x.getStatus(), ComponentVersionStatusEnum.NEW.getStatus()))
                .findFirst().orElse(null);

        log.info("[新增版本]规则编码:{},当前已有版本:{}", ruleInfo.getCode(), ruleVersionVos);
        //1.新建版本，直接新增
        if (CollectionUtils.isEmpty(ruleVersionVos)) {
            this.save(version);
            addVersionDetail(version);
            //保存动作配置
            componentActionService.saveOrUpdateComponentAction(
                    version.getComponentActionList(),
                    version.getRuleCode(),
                    ComponentTypeEnum.COMPLEX_RULE_BASE.getType(),
                    SysConstant.ONE);
            return version.getId();
        }

        //2.新增版本,有【新建】状态版本覆盖，没有则新增
        if (Objects.isNull(newVersion)) {
            Long newVersionNo = versionMapper.getMaxVersionNo(ruleInfo.getId()) + 1;
            version.setVersionNo(newVersionNo);
            version.setStatus(ComponentVersionStatusEnum.NEW.getStatus());
            this.save(version);
            addVersionDetail(version);
            //保存动作配置
            if (CollectionUtils.isEmpty(version.getComponentActionList())) {
                version.getComponentActionList().forEach(x -> x.setVersionNo(newVersionNo).setId(null));
            }
            componentActionService.saveOrUpdateComponentAction(
                    version.getComponentActionList(),
                    version.getRuleCode(),
                    ComponentTypeEnum.COMPLEX_RULE_BASE.getType(),
                    newVersionNo);
        } else {
            version.setId(newVersion.getId());
            version.setVersionNo(newVersion.getVersionNo());
            if (!CollectionUtils.isEmpty(version.getComponentActionList())) {
                version.getComponentActionList().forEach(x -> x.setId(null));
            }
            updateVersion(version);
        }
        return version.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long addVersion(RuleVersionVo version) {
        if (StringUtils.isEmpty(version.getRuleCode())) {
            throw new ApiException(PARAMS_EXCEPTION.getCode(), PARAMS_EXCEPTION.getMessage());
        }
        RuleInfo ruleInfo = ruleService.queryByCode(version.getRuleCode());
        if (ruleInfo == null) {
            throw exception(COMPONENT_NOT_EXISTS, version.getRuleCode());
        }
        //更新规则的更新人，更新时间
        ruleInfo.setUpdatedTime(DateUtil.now());
        ruleInfo.setUpdateUserId(SessionManager.getLoginUserId());
        if (!ruleService.updateById(ruleInfo)) {
            throw new ApiException(ErrorCodeEnum.DECISION_VERSION_NEW_ERROR);
        }

        //填充规则属性值
        version.setRuleId(ruleInfo.getId());

        if (version.getStatus() == null) {
            version.setStatus(ComponentVersionStatusEnum.NEW.getStatus());
        }
        if (version.getVersionNo() == null) {
            Long maxVersionNo = versionMapper.getMaxVersionNo(ruleInfo.getId());
            version.setVersionNo(maxVersionNo + 1);
        }

        this.save(version);
        addVersionDetail(version);
        componentActionService.saveOrUpdateComponentAction(
                version.getComponentActionList(),
                version.getRuleCode(),
                ComponentTypeEnum.COMPLEX_RULE_BASE.getType(),
                version.getVersionNo());
        return version.getId();
    }

    @Transactional(rollbackFor = Exception.class)
    public boolean addVersionDetail(RuleVersionVo version) {
        List<RuleBlockVo> ruleBlockVoList = version.getRuleBlockVoList();
        for (RuleBlockVo ruleBlockVo : ruleBlockVoList) {
            RuleBlock ruleBlock = new RuleBlock();
            BeanUtils.copyProperties(ruleBlockVo, ruleBlock);
            ruleBlock.setRuleId(version.getRuleId());
            ruleBlock.setVersionId(version.getId());
            ruleBlockService.save(ruleBlock);

            RuleConditionVo ruleConditionVo = ruleBlockVo.getRuleConditionVo();
            ruleConditionVo.setVersionId(version.getId());
            ruleConditionVo.setBlockId(ruleBlock.getId());
            //添加条件信息
            conditionService.insertRuleCondition(ruleConditionVo, version.getRuleId(), ruleBlock.getId());
            //添加输出字段
            List<StrategyOutput> strategyOutputList = ruleBlockVo.getStrategyOutputList();
            if (strategyOutputList != null && strategyOutputList.size() > 0) {
                strategyOutputService.insertTacticsOutput(ruleBlock.getId(), strategyOutputList);
            }
            List<StrategyOutput> failOutputList = ruleBlockVo.getFailOutputList();
            if (failOutputList != null && failOutputList.size() > 0) {
                strategyOutputService.insertTacticsOutput(ruleBlock.getId(), failOutputList);
            }
        }
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateVersion(RuleVersionVo version) {
        Long ruleId = version.getRuleId();
        Long versionId = version.getId();
        if (versionId == null) {
            return false;
        }
        RuleVersion ruleVersion = this.getById(versionId);
        if (ruleVersion == null) {
            throw new ApiException(PARAMS_EXCEPTION.getCode(), ErrorCodeEnum.PARAMS_EXCEPTION.getMessage());
        }
        if (!ComponentVersionStatusEnum.NEW.getStatus().equals(ruleVersion.getStatus())) {
            throw new ApiException(
                    GENERAL_RULE_UPDATE_STATUS_ERROR.getCode(),
                    GENERAL_RULE_UPDATE_STATUS_ERROR.getMessage() + ",当前版本状态：" + ComponentVersionStatusEnum.get(ruleVersion.getStatus()).getName());
        }

        // 1. 修改版本主表
        versionMapper.updateById(version);

        // 2. 删除原版本下所有子表数据
        // 删除所有规则块
        ruleBlockService.removeObjects(ruleId, versionId);
        // 删除所有条件
        conditionService.deleteRuleCondition(ruleId, versionId);
        // 删除所有策略输出（需找出原来的所有块，然后逐一删除）
        List<RuleBlock> oldRuleBlockList = ruleBlockService.listObjectsByVersionId(versionId);
        for (RuleBlock ruleBlock : oldRuleBlockList) {
            StrategyOutput strategyOutput = new StrategyOutput(ruleBlock.getId(), ComponentTypeEnum.COMPLEX_RULE_BASE.getType());
            strategyOutputService.deleteByTactics(strategyOutput);
        }

        // 3. 插入新版本下子表数据
        List<RuleBlockVo> ruleBlockVoList = version.getRuleBlockVoList();
        for (RuleBlockVo ruleBlockVo : ruleBlockVoList) {
            // 修改规则块配置表
            ruleBlockVo.setRuleId(ruleId);
            ruleBlockVo.setVersionId(versionId);
            ruleBlockService.save(ruleBlockVo);
            Long blockId = ruleBlockVo.getId();
            //修改条件表
            conditionService.updateRuleCondition(ruleId, blockId, ruleBlockVo.getRuleConditionVo());
            //修改策略输出
            strategyOutputService.updateTacticsOutput(blockId, ruleBlockVo.getStrategyOutputList(), ruleBlockVo.getFailOutputList(), ComponentTypeEnum.COMPLEX_RULE_BASE.getType());
        }

        List<ComponentAction> componentActionList = version.getComponentActionList();
        if (!CollectionUtils.isEmpty(componentActionList)) {
            componentActionList.forEach(x ->
                    x.setComponentCode(version.getRuleCode())
                            .setComponentType(ComponentTypeEnum.COMPLEX_RULE_BASE.getType())
                            .setVersionNo(version.getVersionNo()));
        }
        componentActionService.saveOrUpdateComponentAction(
                version.getComponentActionList(),
                version.getRuleCode(),
                ComponentTypeEnum.COMPLEX_RULE_BASE.getType(),
                version.getVersionNo());
        return true;
    }

    @Override
    @Transactional
    public boolean updateStatus(StatusParam statusParam) {
        LambdaQueryWrapper<RuleVersion> updateWrapper = new LambdaQueryWrapper<>();
        updateWrapper.in(RuleVersion::getId, statusParam.getIds());
        updateWrapper.eq(RuleVersion::getRuleId, statusParam.getStrategyId());
        RuleVersion ruleVersion = new RuleVersion();
        ruleVersion.setStatus(statusParam.getStatus());
        boolean update = this.update(ruleVersion, updateWrapper);
        return update;
    }

    @Override
    public void lock(Long versionId) {
        RuleVersionVo ruleVersionVo = queryById(versionId);
        if (ruleVersionVo == null) {
            throw exception(COMPONENT_NOT_EXISTS, versionId);
        }

        if (!Objects.equals(ruleVersionVo.getStatus(), ComponentVersionStatusEnum.NEW.getStatus())) {
            throw exception(COMPONENT_VERSION_LOCK_ERROR_1, ComponentVersionStatusEnum.getNameByStatus(ruleVersionVo.getStatus()));
        }

        RuleInfo ruleInfo = ruleService.queryByCode(ruleVersionVo.getRuleCode());
        if (ruleInfo == null) {
            throw exception(COMPONENT_NOT_EXISTS, ruleVersionVo.getRuleCode());
        }

        //校验当前版本信息完整性
        Set<String> emptyFieldSet = new HashSet<>();
        ComponentCommonContext.checkParamNotEmpty(ruleVersionVo, emptyFieldSet);
        if (!CollectionUtils.isEmpty(emptyFieldSet)) {
            log.info(ErrorCodeEnum.INCOMPLETE_COMPONENT_INFORMATION.getMessage() + ",versionId:{},emptyFieldSet:{}",
                    versionId, JSON.toJSONString(emptyFieldSet));
            throw new ApiException(ErrorCodeEnum.INCOMPLETE_COMPONENT_INFORMATION);
        }

        ruleVersionVo.setStatus(ComponentVersionStatusEnum.ENABLE.getStatus());
        transactionTemplate.execute(status -> {
            try {
                this.updateById(ruleVersionVo);
                ruleInfo.setStatus(ComponentStatusEnum.ENABLE.getStatus());
                ruleService.updateById(ruleInfo);
                return Boolean.TRUE;
            } catch (Exception e) {
                status.setRollbackOnly();
                log.error("锁定版本失败，版本id==>{}", versionId, e);
                throw exception(ErrorCodeEnum.COMPONENT_VERSION_LOCK_ERROR_2, ruleVersionVo.getVersionNo());
            }
        });
    }

}
